/* vcc/crypt.c 
 * 
 * This file is part of vcc. 
 * 
 * vcc is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * vcc is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with vcc. If not, see <https://www.gnu.org/licenses/>
 */ 




#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/times.h>
#include <gcrypt.h>

#include <vcc/vcc.h>
#include <vcc/plugin.h>
#include <vcc/version.h>
#include <vcc/pretty.h>
#include <vcc/interfaces.h>

/* maybe because of that i'm not good at crypting (in fact, i even don't 
 * know eular etc. ), i think the libgcrypt is the most ****ingest library 
 * that i've used. it's a great gnu library, but the document is **too** 
 * ****ing ****ing, that you can't know what you're doing. */


#define CIPHER_ALGO 	GCRY_CIPHER_AES256
#define show_err(err) 	fprintf(stderr, "error: %s: %s\n", \
		gcry_strsource(err), gcry_strerror(err))


int 			keysize, blksize;
int 			msgsize;
int 			crypt_disabled = 0;

char 			*key, *iv;
gcry_cipher_hd_t 	hd;


int show_algo_info(void) {
	keysize = gcry_cipher_get_algo_keylen(CIPHER_ALGO);
	blksize = gcry_cipher_get_algo_blklen(CIPHER_ALGO);

	msgsize = MSG_SIZE / blksize * blksize;

	if (unlikely(!(key = malloc(keysize))) || unlikely(!(iv = malloc(keysize)))) {
		fprintf(stderr, "*** malloc() failed\n");

		return 1;
	}

	printf(_("key size: %d\n"), keysize);
	printf(_("block size: %d\n"), blksize);
	printf(_("maximum message size: %d\n"), msgsize);

	return 0;
}

int crypt_init(void *k, void *v) {
	gcry_error_t err;

	memcpy(key, k, keysize);
	memcpy(iv, v, blksize);

	printf("libgcrypt %s\n", gcry_check_version(NULL));

	if (unlikely((err = gcry_cipher_open(&hd, CIPHER_ALGO, GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_CTS)))) {
		show_err(err);

		return 1;
	}

	if (unlikely((err = gcry_cipher_setkey(hd, key, keysize)))) {
		show_err(err);

		return 1;
	}

	if (unlikely((err = gcry_cipher_setiv(hd, iv, blksize)))) {
		show_err(err);

		return 1;
	}

	return 0;
}


int encrypt(char *msg) {
	gcry_error_t err;

	if (crypt_disabled) {
		fprintf(stderr, _("error: crypting disabled. \n"));

		return 1;
	}

	if (unlikely((err = gcry_cipher_reset(hd)))) {
		show_err(err);

		return 1;
	}

	if (unlikely((err = gcry_cipher_encrypt(hd, msg, msgsize, NULL, 0)))) {
		show_err(err);

		return 1;
	}

	return 0;
}


int decrypt(char *msg) {
	gcry_error_t err;

	if (crypt_disabled) {
		fprintf(stderr, _("error: crypt disabled. \n"));

		return 1;
	}
	
	if (unlikely((err = gcry_cipher_reset(hd)))) {
		show_err(err);

		return 1;
	}

	if (unlikely((err = gcry_cipher_decrypt(hd, msg, msgsize, NULL, 0)))) {
		show_err(err);

		return 1;
	}

	return 0;
}

